/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Random Theme Switcher code
 *
 * Portions created by the Initial Developer are Copyright (C) 2005-2007
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   O. Atsushi <torisugari@gmail.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

const CLASSID = Components.ID("{ce7762dd-9C5f-403e-9324-6fa9d90b5c75}");
const CONTRACTID = "@addons.mozilla.org/extensions/randomtheme;1";
const CLASSNAME = "Random Theme Switcher";

function ClassImpl(){
  var observerService =
    Components.classes["@mozilla.org/observer-service;1"]
              .getService(Components.interfaces.nsIObserverService);

  observerService.addObserver(this, "xpcom-shutdown", false);
  observerService.addObserver(this, "quit-application", false);
}

ClassImpl.prototype = {
  QueryInterface: function(iid) {
    if (!iid.equals(Components.interfaces.nsIObserver) &&
        !iid.equals(Components.interfaces.nsISupports))
      throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  },

  observe: function(aSubject, aTopic, aData) {
    switch (aTopic) {
    case "quit-application":
/*
      if (aData != "restart")
        Components.utils.reportError("FOO");
*/
      this.doSwitch();
      break;
    case "xpcom-shutdown":
      this._shutdown();
      break;
    }
  },

  _shutdown: function(){
    var osvc = Components.classes["@mozilla.org/observer-service;1"]
                         .getService(Components.interfaces.nsIObserverService);
    osvc.removeObserver(this, "quit-application");
    osvc.removeObserver(this, "xpcom-shutdown");

    this._pref = null;
  },


//internal functions
  _pref: null,

  _currentThemeIndex: 0,

  get extensionID(){
    return "urn:mozilla:extension:{ce7762dd-9C5f-403e-9324-6fa9d90b5c75}";
  },

  doSwitch: function() {
    this._pref= Components.classes["@mozilla.org/preferences-service;1"]
                          .getService(Components.interfaces.nsIPrefBranch);

    //If the user has already specified the next theme in this browsing
    //session, this component should not overwrite that choice.
    if (this._pref.getBoolPref("extensions.dss.switchPending"))
      return;

    var ds= Components.classes["@mozilla.org/extensions/manager;1"]
                      .getService(Components.interfaces.nsIExtensionManager)
                      .datasource;

    var rdfService = Components.classes["@mozilla.org/rdf/rdf-service;1"]
                               .getService(Components.interfaces.nsIRDFService);
    var container = Components.classes["@mozilla.org/rdf/container;1"]
                              .getService(Components.interfaces
                                                    .nsIRDFContainer);
    var internalNameArc =
      rdfService.GetResource("http://www.mozilla.org/2004/em-rdf#internalName");
    var typeArc =
      rdfService.GetResource("http://www.mozilla.org/2004/em-rdf#type");
    var idArc =
      rdfService.GetResource("http://www.mozilla.org/2004/em-rdf#id");
    var disabledArc =
      rdfService.GetResource("http://www.mozilla.org/2004/em-rdf#disabled");

    //Check if this extension is disabled or not. This is another users choice.
    //Don't bother them per forcing restarting twice, when they find their
    //favorite themes.


    //XXX nsIObserver is aother solution. Try it someday.
    var switcher = rdfService.GetResource(this.extensionID);
    var disabledTarget = ds.GetTarget(switcher, disabledArc, true);
    if (disabledTarget &&
        (disabledTarget.QueryInterface(Components.interfaces.nsIRDFLiteral)
                       .Value == "true"))
      return;

    //Get ready to iterate themes.
    var itemRoot = rdfService.GetResource("urn:mozilla:item:root");

    var excludeDefault = this._pref.getBoolPref("randomtheme.skipDefault");
    var excludeCurrent = this._pref.getBoolPref("randomtheme.skipCurrent");

    var defaultTheme = "classic/1.0";
    var currentTheme = this._currentTheme;

    container.Init(ds, itemRoot);
    var elements = container.GetElements();


    if (this._pref.getBoolPref("randomtheme.randomOrder")) {
      /* The mode is "In Random Order"*/
      var themes = [];
      while (elements.hasMoreElements()) {
        var theme = null;
        var type = 0;
        var element =
          elements.getNext()
                  .QueryInterface(Components.interfaces.nsIRDFResource);

        //Get item type 2:extension, 4:theme, etc.
        var target = ds.GetTarget(element, typeArc, true);
        if (target)
          type = target.QueryInterface(Components.interfaces.nsIRDFInt).Value;

        //Get Internal Name
        target = ds.GetTarget(element, internalNameArc, true);
        if (target)
          theme = 
            target.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;

        if (theme && (type == 4) &&
            !(excludeDefault && theme == defaultTheme) &&
            !(excludeCurrent && theme == currentTheme)) {
          themes.push(theme);
        }
      }

      if (themes.length < 1)
        return;

      var randomIndex = parseInt(Math.random() * themes.length);
      this._nextTheme = themes[randomIndex];
    }
    else {
      /* The mode is "In Listed Order"*/
      var index = 0;
      var currentIndex = -1;
      var firstTheme = null;
      var nextTheme = null;

      while (elements.hasMoreElements()) {
        var theme = null;
        var type = 0;
        var element =
          elements.getNext()
                  .QueryInterface(Components.interfaces.nsIRDFResource);

        //Get item type 2:extension, 4:theme, etc.
        var target=ds.GetTarget(element, typeArc, true);
        if (target)
          type = target.QueryInterface(Components.interfaces.nsIRDFInt).Value;

        //Get Internal Name
        target = ds.GetTarget(element, internalNameArc, true);
        if (target)
          theme =
            target.QueryInterface(Components.interfaces.nsIRDFLiteral).Value;

        if (theme && (type == 4) &&
            !(excludeDefault && theme == defaultTheme)) {
          if (index == 0)
            firstTheme = theme;
          if (theme == currentTheme)
            currentIndex = index;
          if ((currentIndex > -1) && (index == (currentIndex + 1))) {
            nextTheme = theme;
            break;
          }
          index++;
        }
      }
      if ((index > 0) && !nextTheme)
        nextTheme = firstTheme;
      this._nextTheme = nextTheme;
    }
  },

  get _currentTheme() {
    if (this._pref.prefHasUserValue("general.skins.selectedSkin"))
      return this._pref.getCharPref("general.skins.selectedSkin");
    return "classic/1.0";
  },

  set _nextTheme(aInternalName) {
    if (!aInternalName)
      return;
    this._pref.setCharPref("extensions.lastSelectedSkin", aInternalName);
    this._pref.setBoolPref("extensions.dss.switchPending", true);
  }
};

function GenericComponentFactory(aCtor) {
  this._ctor = aCtor;
}

GenericComponentFactory.prototype = {

  _ctor: null,

  // nsIFactory
  createInstance: function C_ci(aOuter, aIID) {
    if (aOuter)
      throw Components.results.NS_ERROR_NO_AGGREGATION;
    return (new this._ctor()).QueryInterface(aIID);
  },

  // nsISupports
  QueryInterface: function C_qi(aIID) {
    if (aIID.equals(Components.interfaces.nsIFactory) ||
        aIID.equals(Components.interfaces.nsISupports))
      return this;
    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
};

var gModule = {
  QueryInterface: function M_qi(aIID) {
    if (aIID.equals(Components.interfaces.nsIModule) ||
        aIID.equals(Components.interfaces.nsISupports))
      return this;

    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  getClassObject: function M_co(aCM, aCID, aIID) {
    if (!aIID.equals(Components.interfaces.nsIFactory))
      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
    if (aCID.equals(CLASSID))
      return new GenericComponentFactory(ClassImpl);

    throw Components.results.NS_ERROR_NO_INTERFACE;
  },

  registerSelf: function M_rs(aCM, aFile, aLocation, aType) {
    aCM.QueryInterface(Components.interfaces.nsIComponentRegistrar)
       .registerFactoryLocation(CLASSID, CLASSNAME, CONTRACTID,
                                aFile, aLocation, aType);

    /* call nsIObserver:observes(...) with "app-startup" */

    this.categoryManager
        .addCategoryEntry("app-startup", CLASSNAME, "service," + CONTRACTID,
                          true, true, null);
  },

  unregisterSelf: function M_us(aCM, aLocation, aType) {
    aCM.QueryInterface(Components.interfaces.nsIComponentRegistrar)
       .unregisterFactoryLocation(CLASSID, location);

    this.categoryManager.deleteCategoryEntry("app-startup", CLASSNAME, true);
  },

  get categoryManager() {
    return Components.classes["@mozilla.org/categorymanager;1"]
                     .getService(Components.interfaces.nsICategoryManager);

  },

  canUnload: function M_canUnload(aCM) {
    return true;
  }
};

function NSGetModule(aCM, aFile) {
  return gModule;
}
